#region Debug Preprocessor
#if !DEBUG || !TRACE
    // If not debug then take out all debugging.
    #define CPU_DEBUG_OFF
    #undef CPU_DEBUG_ALL
#endif

// Check if both are defined and raise error.
#if CPU_DEBUG_ALL && CPU_DEBUG_OFF
    #error CPU_DEBUG_ALL and CPU_DEBUG_OFF cannot both be defined
#endif

// Define cpu debug if any of the subsections debuging are turned on.
#if (CPU_MEM_DEBUG || CPU_INT_DEBUG || CPU_STACK_DEBUG || CPU_INS_DEBUG || CPU_TIMING_DEBUG || CPU_OP_FIRST_CALL_DEBUG) && !CPU_DEBUG_ALL && !CPU_DEBUG_OFF
#define CPU_DEBUG
#endif

// Turn on all cpu debug.
#if CPU_DEBUG_ALL
    #define CPU_DEBUG
    #define CPU_MEM_DEBUG
    #define CPU_INT_DEBUG
    #define CPU_INS_DEBUG
    #define CPU_STACK_DEBUG
    #define CPU_TIMING_DEBUG
#endif

// Turn of all cpu debug.
#if CPU_DEBUG_OFF
    #undef CPU_DEBUG
    #undef CPU_MEM_DEBUG
    #undef CPU_INT_DEBUG
    #undef CPU_INS_DEBUG
    #undef CPU_STACK_DEBUG
    #undef CPU_TIMING_DEBUG
#endif


#endregion Debug Preprocessor

using System;
using System.Collections.Generic;
using System.Text;
using Zsa.Emulation.Util;
using System.Diagnostics;

namespace Zsa.Emulation.Processor
{
    /// <summary>
    /// Emulates the nes main cpu.
    /// </summary>
    public partial class Cpu6502
    {
        #region Fields

        #region Registers
        /// <summary>
        /// RegisterX
        /// </summary>
        private byte _RegisterX;

        /// <summary>
        /// Register Y.
        /// </summary>
        private byte _RegisterY;

        /// <summary>
        /// The accumulator register.
        /// </summary>
        private byte _RegisterAccumulator;

        /// <summary>
        /// The program register.
        /// </summary>
        private ushort _RegisterProgramCounter;

        /// <summary>
        /// The stack pointer.
        /// </summary>
        private byte _RegisterStackPointer;

        /// <summary>
        /// The status register.
        /// </summary>
        private StatusRegisterFlags _RegisterStatus = StatusRegisterFlags.Reserved; 
        #endregion Registers


        /// <summary>
        /// The pending interrupt if any.
        /// </summary>
        private InterruptType _PendingInterrupt = InterruptType.Unknown;

        /// <summary>
        /// The current interrupt.
        /// </summary>
        private InterruptType _CurrentInterrupt = InterruptType.Unknown;

        /// <summary>
        /// The memory mapper.
        /// </summary>
        private CpuMemoryMap _Memory;

        /// <summary>
        /// Temp buffer.
        /// </summary>
        private byte[] _BufferTemp;

        /// <summary>
        /// True if dma.
        /// </summary>
        internal bool _IsDma = false;

        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="Cpu6502"/> class.
        /// </summary>
        /// <param name="mem">The mem.</param>
        public Cpu6502(CpuMemoryMap mem)
        {
            if (mem == null)
            {
                throw new ArgumentNullException();
            }
            _Memory = mem;
        }
        #endregion Constructor

        #region Properties

        /// <summary>
        /// Gets the current stack position.
        /// </summary>
        /// <value>The current stack position.</value>
        internal ushort CurrentStackPosition
        {
            get
            {
                return (ushort)(_RegisterStackPointer + 0x100);
            }
            set
            {
                if (value < 0x100 || value > 0x1FF)
                {
                    throw new ArgumentOutOfRangeException(
                        "value",
                        value,
                        "The value must be in the zero page between 0x100-0x1FF !"
                        );
                }
                _RegisterStackPointer = (byte)(value - 0x100);
            }
        }


        /// <summary>
        /// Reads the address of the item on the top of the stack.
        /// </summary>
        internal ushort CurrentStackAddress
        {
            get
            {
                // Get the address.  Add the zero page.
                int _Address = CurrentStackPosition;
                //Read the address.
                return (ushort)((_Memory.Read(_Address+2)) | _Memory.Read(_Address + 1));
            }
        }


        /// <summary>
        /// Reads the accumulator register value and looks at the current status
        /// flags and returns the accumaltor value as a short.  
        /// </summary>
        /// <remarks>
        /// Value is 10 bit signed number.
        /// 1 byte number.  1 byte carry bit in status register, and 1 bit to sign.
        /// </remarks>
        /// <returns></returns>
        internal short RegisterAccumlator
        {
            get
            {
                if ((_RegisterStatus & StatusRegisterFlags.Sign) == StatusRegisterFlags.Sign)
                {
                    return (short)(_RegisterAccumulator |
                    ((_RegisterStatus & StatusRegisterFlags.CarryFlag)
                        == StatusRegisterFlags.CarryFlag ? 0x0100 : 0) * -1);

                    // | 0x80000000;
                }
                return (short)(_RegisterAccumulator |
                    ((_RegisterStatus & StatusRegisterFlags.CarryFlag)
                        == StatusRegisterFlags.CarryFlag ? 0x0100 : 0));
            }
            set
            {
                if (value == 0)
                {
                    _RegisterStatus =
                        StatusRegisterFlags.ZeroFlag |
                        (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                        (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                        (_RegisterStatus & StatusRegisterFlags.Decimal);
                    _RegisterAccumulator = 0;
                }
                else if (value >= 0)
                {
                    if (value <= 255)
                    {
                        _RegisterStatus =
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = (byte)(0xFF & value);
                    }
                    else if (value < 512)
                    {
                        _RegisterStatus =
                            StatusRegisterFlags.CarryFlag |
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = (byte)(0xFF & value);
                    }
                    else
                    {
                        _RegisterStatus =
                            StatusRegisterFlags.Overflow |
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = 0;
                    }
                }
                else //if (value < 0)
                {
                    if (value > -255)
                    {
                        _RegisterStatus =
                            StatusRegisterFlags.Sign |
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = (byte)(0xFF & value);
                    }
                    else if (value < -511)
                    {
                        _RegisterStatus =
                            StatusRegisterFlags.Sign | StatusRegisterFlags.CarryFlag |
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = (byte)(0xFF & value);
                    }
                    else
                    {
                        _RegisterStatus =
                            StatusRegisterFlags.Overflow |
                            (_RegisterStatus & StatusRegisterFlags.InterruptFlag) |
                            (_RegisterStatus & StatusRegisterFlags.Break) | StatusRegisterFlags.Reserved |
                            (_RegisterStatus & StatusRegisterFlags.Decimal);
                        _RegisterAccumulator = 0;
                    }
                }
            }

        }

        #region Status Register Flags
        /// <summary>
        /// Gets or sets a value indicating whether [interrupts enabled].
        /// </summary>
        /// <value><c>true</c> if [interrupts enabled]; otherwise, <c>false</c>.</value>
        internal bool InterruptsEnabled
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.InterruptFlag) == StatusRegisterFlags.InterruptFlag;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.InterruptFlag;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.InterruptFlag) == StatusRegisterFlags.InterruptFlag)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.InterruptFlag;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [carry flag].
        /// </summary>
        /// <value><c>true</c> if [carry flag]; otherwise, <c>false</c>.</value>
        internal bool CarryFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.CarryFlag) == StatusRegisterFlags.CarryFlag;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.CarryFlag;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.CarryFlag) == StatusRegisterFlags.CarryFlag)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.CarryFlag;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [break flag].
        /// </summary>
        /// <value><c>true</c> if [break flag]; otherwise, <c>false</c>.</value>
        internal bool BreakFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.Break) == StatusRegisterFlags.Break;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.Break;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.Break) == StatusRegisterFlags.Break)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.Break;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [over flow flag].
        /// </summary>
        /// <value><c>true</c> if [over flow flag]; otherwise, <c>false</c>.</value>
        internal bool OverFlowFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.Overflow) == StatusRegisterFlags.Overflow;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.Overflow;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.Overflow) == StatusRegisterFlags.Overflow)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.Overflow;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [decimal flag].
        /// </summary>
        /// <value><c>true</c> if [decimal flag]; otherwise, <c>false</c>.</value>
        internal bool DecimalFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.Decimal) == StatusRegisterFlags.Decimal;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.Decimal;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.Decimal) == StatusRegisterFlags.Decimal)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.Decimal;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [zero status flag].
        /// </summary>
        /// <value><c>true</c> if [zero status flag]; otherwise, <c>false</c>.</value>
        internal bool ZeroStatusFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.ZeroFlag) == StatusRegisterFlags.ZeroFlag;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.ZeroFlag;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.ZeroFlag) == StatusRegisterFlags.ZeroFlag)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.ZeroFlag;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [signed status flag].
        /// </summary>
        /// <value><c>true</c> if [signed status flag]; otherwise, <c>false</c>.</value>
        internal bool SignedStatusFlag
        {
            get
            {
                return (_RegisterStatus & StatusRegisterFlags.Sign) == StatusRegisterFlags.Sign;
            }
            set
            {
                if (value)
                {
                    _RegisterStatus |= StatusRegisterFlags.Sign;
                }
                else
                {
                    if ((_RegisterStatus & StatusRegisterFlags.Sign) == StatusRegisterFlags.Sign)
                    {
                        _RegisterStatus ^= StatusRegisterFlags.Sign;
                    }
                }
            }
        }

        #endregion Status Register Flags


        #endregion Properties

        #region Methods
        /// <summary>
        /// Clears all the registers.
        /// </summary>
        internal void ClearRegisters()
        {
            _RegisterAccumulator = 0;
            _RegisterProgramCounter = 0;
            _RegisterStackPointer = 0xFF;
            _RegisterStatus = StatusRegisterFlags.Reserved;
            _RegisterX = 0;
            _RegisterY = 0;
            _MainMemory = _Memory.MainMemoryBuffer;
        }

        /// <summary>
        /// Sets the interrupt.
        /// </summary>
        /// <param name="interrupt">The interrupt.</param>
        public void SetInterrupt(InterruptType interrupt)
        {
            if (_MainMemory == null)
            {
                _MainMemory = _Memory.MainMemoryBuffer;
            }
            //if (interrupt == InterruptType.Reset)
            //{
            //    ClearRegisters();
            //}
            _PendingInterrupt = interrupt;
        }

        /// <summary>
        /// Gets the cpu state.
        /// </summary>
        /// <returns></returns>
        public Cpu6502State GetState()
        {
            return new Cpu6502State(
                _RegisterProgramCounter,
                _RegisterAccumulator,
                _RegisterX,
                _RegisterY,
                _RegisterStackPointer,
                _RegisterStatus
                );
                
        }
        #endregion Methods
    }
}